from __future__ import with_statement

import transaction
import time
from crv.model import DBSession, metadata
from crv.model.crvmodel import *

import logging
log = logging.getLogger(__name__)

from threading import Thread, Lock
from collections import deque

from crv.tools.singleton import Singleton
from crv.tools.statemachinemgr import StateMachineMgr

def checkState_INITIALIZED_VM(vm):
    vmMgr=VMMgr.getInstance()
    vmstatus=vmMgr.getVMMaker(vm).getStatus(vm.getAttr("identifier"))
    if vmstatus=="terminated" or vmstatus=="UNDEFINED":
        return True
    return False

def checkState_STARTING_VM(vm):
    vmMgr=VMMgr.getInstance()
    log.debug("checkState_STARTING_VM::checking vm=%s, identifier=%s"%(str(vm),str(vm.getAttr("identifier"))))
    
    vmstatus=vmMgr.getVMMaker(vm).getStatus(vm.getAttr("identifier"))
    if (vmstatus=="pending" or vmstatus=="running") and not vm.getStarted():
        return True
    return False

#if vmMgr.getVMMaker(vm).isStarting(vm.getAttr("identifier")) or vmMgr.getVMMaker(vm).isStarted(vm.getAttr("identifier")):
        #VM is being started or already started. E.g. euca-run-instance has been called and returned an identifier
#        if vm.getStarted():
#            log.debug("checkState_STARTING_VM::found Started=True, returning false")
#            #This should be started
#            return False
#        log.debug("checkState_STARTING_VM::returning True")            
#        return True
    #vm.getAttr("identifier") not in list
#    log.debug("checkState_STARTING_VM::returning False")                
#    return False

def checkState_STARTED_VM(vm):
    vmMgr=VMMgr.getInstance()
    vmstatus=vmMgr.getVMMaker(vm).getStatus(vm.getAttr("identifier"))
    if (vmstatus!="shutting-down" and vmstatus!="terminated" and vmstatus!="UNDEFINED" ) and vm.getStarted():
        return True
    return False

#if vmMgr.getVMMaker(vm).isStarted(vm.getAttr("identifier")):
        #VM is being started or already started. E.g. euca-run-instance has been called and returned an identifier
#        if vm.getStarted():
#            #This should be started
#            log.debug("checkState_STARTED_VM::returning True")
#            return True
#    log.debug("checkState_STARTED_VM::returning False")        
#    return False

def checkState_STOPPING_VM(vm):
    vmMgr=VMMgr.getInstance()
    vmstatus=vmMgr.getVMMaker(vm).getStatus(vm.getAttr("identifier"))
    if (vmstatus=="shutting-down"):
        return True
    return False

#vmMgr=VMMgr.getInstance()
#    return vmMgr.getVMMaker(vm).isStopping(vm.getAttr("identifier"))
    #in isStopping: will 1. do euca-describe-instance to see if it get any result with "terminating" or "running". 2. If yes, return false; 3. if no, remove identifier from the list, return true
    
def checkState_EXCEPTION_VM(vm):
    return False
    
def checkState_FIXING_VM(vm):
    return False
    
#canAdvance is only needed for those state transitions that need to control its children, e.g. INITIALIZED->STARTING, need to loop over roles and set targetSC=STARTED
#setting to exception states are automatic

def canAdvance_INITIALIZED_STARTING_VM(vm):
    #if it's already in initialized, it should be able to start right away
    vmMgr=VMMgr.getInstance()
    return vmMgr.getVMMaker(vm).ready()

def doAdvance_INITIALIZED_STARTING_VM(vm):
    vmMgr=VMMgr.getInstance()
    (clientuuid,clientfile)=vmMgr.getClientMaker(vm).makeClient(vm)
    vm.setAttr("identifier",vmMgr.getVMMaker(vm).powerOn(clientfile))
    vmMgr.getClientMaker(vm).expireClient(clientuuid)
    #FIXME:vm.clean all attr
    log.debug("doAdvance_INITIALIZED_STARTING_VM::started vm identifier=%s"%str(vm.getAttr("identifier")))

def canAdvance_STARTED_STOPPING_VM(vm):
    return True

def doAdvance_STARTED_STOPPING_VM(vm):
    vmMgr=VMMgr.getInstance()
    vmMgr.getVMMaker(vm).powerOff(vm.getAttr("identifier"))
    vm.setStarted(False)
    vm.setAttr("PublicIPV4","")
    vm.setAttr("PrivateIPV4", "")
    vm.setAttr("Identifier","")

class VMMgr(Singleton):
    """
    @author Yushu Yao
    @version 0.1
    """

    def __init__(self):
        super(VMMgr, self).__init__()
        self.__smgr = StateMachineMgr()
        
        self.__smgr.addNodes(VM.SCname.keys())
        self.__smgr.addEdge(VM.SC.SC_INITIALIZED, VM.SC.SC_STARTING)
        self.__smgr.addEdge(VM.SC.SC_STARTING, VM.SC.SC_STARTED)
        self.__smgr.addEdge(VM.SC.SC_STARTING, VM.SC.SC_EXCEPTION)
        self.__smgr.addEdge(VM.SC.SC_STARTED, VM.SC.SC_STOPPING)
        self.__smgr.addEdge(VM.SC.SC_STARTED, VM.SC.SC_EXCEPTION)
        self.__smgr.addEdge(VM.SC.SC_STOPPING, VM.SC.SC_INITIALIZED)
        self.__smgr.addEdge(VM.SC.SC_STOPPING, VM.SC.SC_EXCEPTION)
        self.__smgr.addEdge(VM.SC.SC_EXCEPTION, VM.SC.SC_FIXING)
        self.__smgr.addEdge(VM.SC.SC_FIXING, VM.SC.SC_INITIALIZED)

        self.__smgr.setFunc_checkState(VM.SC.SC_INITIALIZED,checkState_INITIALIZED_VM)
        self.__smgr.setFunc_checkState(VM.SC.SC_STARTING , checkState_STARTING_VM )
        self.__smgr.setFunc_checkState(VM.SC.SC_STARTED , checkState_STARTED_VM )
        self.__smgr.setFunc_checkState(VM.SC.SC_STOPPING , checkState_STOPPING_VM )
        self.__smgr.setFunc_checkState(VM.SC.SC_EXCEPTION , checkState_EXCEPTION_VM )
        self.__smgr.setFunc_checkState(VM.SC.SC_FIXING , checkState_FIXING_VM )

        self.__smgr.setFunc_canAdvance(VM.SC.SC_INITIALIZED,VM.SC.SC_STARTING,canAdvance_INITIALIZED_STARTING_VM)
        self.__smgr.setFunc_doAdvance(VM.SC.SC_INITIALIZED,VM.SC.SC_STARTING,doAdvance_INITIALIZED_STARTING_VM)
        self.__smgr.setFunc_canAdvance(VM.SC.SC_STARTED,VM.SC.SC_STOPPING,canAdvance_STARTED_STOPPING_VM)
        self.__smgr.setFunc_doAdvance(VM.SC.SC_STARTED,VM.SC.SC_STOPPING,doAdvance_STARTED_STOPPING_VM)
        
        self.__smgr.initialize(VM.SC, VM.SCname)
        
#    1. NOT NEEDED, checkAll will reload all Roles and all the VMs, RPs it extends. In scheduled task (checkAll) query RP database, and call setRP
#    2. Remove addMaker/addClientMaker
#    3. replace getMaker with getVMMaker, which takes "VM", returns the vm.rp.vmmaker
#    4. Replace getClientMaker with getClientMaker, takes "VM", returns vm.rp.clientmker
#    5. For RoleMgr, do similar, in getProfileMaker, take "Role", return Role.vm.rp.profilemaker
    
    def advanceStates(self,obj):
        return self.__smgr.advanceStates(obj)
    
    def getVMMaker(self,vm):
        return vm.rp.getMaker("vm")
    
    def getClientMaker(self,vm):
        return vm.rp.getMaker("client")

